#pragma once
#include <sys/select.h>
using namespace std;
#include "Socket.hpp"

const int fd_num_max = sizeof(fd_set) * 8;
const int defaultfd = -1;

class SelectServer
{
public:
    SelectServer(uint16_t port = 8888)
        : _port(port)
    {
    }

    bool Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        return true;
    }

    void Start()
    {
        int listensockfd = _listensock.Fd();
        rfds_array[0] = listensockfd;
        // timeout是输入输出参数，可能要进行周期的重复设置
        struct timeval timeout = {5, 0}; // 5s 0us

        for (;;)
        {
            // 每一次都要重新设置要传入到select的rfds的值
            fd_set rfds;
            FD_ZERO(&rfds);
            for (int i = 0; i < fd_num_max; ++i)
            {
                if (defaultfd == rfds_array[i])
                    continue;
                FD_SET(rfds_array[i], &rfds);
            }
            // FD_SET(listensockfd, &rfds);
            int n = select(listensockfd + 1, &rfds, nullptr, nullptr, &timeout); // 最后一个参数是nullptr时为阻塞
            switch (n)
            {
            case 0:
                cout << "time out " << timeout.tv_sec << "," << timeout.tv_usec << endl;
                break;
            case -1:
                cout << "select error" << endl;
                break;
            default:
                HandlerEvent(rfds, listensockfd);
                break;
            }
        }
    }

    void HandlerEvent(fd_set &rfds_, int listensockfd_) // 对套接字进行处理。1、该服务器的套接字listensockfd是否就绪，如果就绪就获取新连接
    {
        for (int i = 0; i < fd_num_max; ++i)
        {
            int fd = rfds_array[i];
            if (fd == defaultfd)
                continue;
            if (FD_ISSET(fd, &rfds_)) // 只处理就绪的文件描述符，包括：套接字连接事件、普通fd的读事件
            {
                if (fd == listensockfd_) // 套接字连接事件就绪
                {
                    string client_ip;
                    uint16_t client_port;
                    int newfd = _listensock.Accept(client_ip, client_port);
                    if (newfd < 0)
                        return;

                    int pos = 1;
                    for (; pos < fd_num_max; ++pos)
                    {
                        if (defaultfd != rfds_array[pos])
                            continue;
                        else
                            break;
                    }
                    if (pos == fd_num_max) //
                    {
                        close(newfd);
                    }
                    else
                        rfds_array[pos] = newfd;
                }
                else // 读事件就绪
                {
                    char inbuffer[1024];
                    int n = read(fd, inbuffer, sizeof(inbuffer) - 1);
                    inbuffer[n] = 0;
                    if (n > 0)
                    {
                        cout << inbuffer << endl;
                    }
                    else if (0 == n) // 客户端关闭
                    {
                        close(fd);
                        rfds_array[i] = defaultfd; // 本质是从select中移除关闭的fd，不在关心该fd（读事件）
                    }
                    else
                    {
                        close(fd);
                        rfds_array[i] = defaultfd;
                    }
                }
            }
        }
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    Sock _listensock;
    uint16_t _port;
    int rfds_array[fd_num_max]; // 这里要把数组rfds_array当作一个与rfds即传入select内部的文件描述符集
};